package org.test.netty;

import static io.netty.handler.codec.http.HttpResponseStatus.OK;
import static io.netty.handler.codec.http.HttpVersion.HTTP_1_1;

import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.test.constants.WsConstant;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.handler.codec.http.DefaultFullHttpResponse;
import io.netty.handler.codec.http.FullHttpRequest;
import io.netty.handler.codec.http.FullHttpResponse;
import io.netty.handler.codec.http.HttpHeaderNames;
import io.netty.handler.codec.http.HttpMethod;
import io.netty.handler.codec.http.HttpResponseStatus;
import io.netty.handler.codec.http.HttpVersion;
import io.netty.handler.codec.http.QueryStringDecoder;
import io.netty.handler.codec.http.multipart.HttpPostRequestDecoder;
import io.netty.handler.codec.http.websocketx.CloseWebSocketFrame;
import io.netty.handler.codec.http.websocketx.PingWebSocketFrame;
import io.netty.handler.codec.http.websocketx.PongWebSocketFrame;
import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;
import io.netty.handler.codec.http.websocketx.WebSocketFrame;
import io.netty.handler.codec.http.websocketx.WebSocketServerHandshaker;
import io.netty.handler.codec.http.websocketx.WebSocketServerHandshakerFactory;
import io.netty.util.Attribute;
import io.netty.util.CharsetUtil;

@Component
public class WebsocketServerHandler extends ChannelInboundHandlerAdapter {
	private  final Logger log=LoggerFactory.getLogger(WebsocketServerHandler.class);
	
	private WebSocketServerHandshaker handshaker;
	//换行符
	public static final String NEWLINE = "\r\n";
	
	/**
	 * 服务器接收请求入口
	 * @param ctx
	 * @param msg
	 * @throws Exception
	 */
	public void channelRead(ChannelHandlerContext ctx, Object msg)
			throws Exception {
		//http请求
		if(msg instanceof  FullHttpRequest) {
			handleHttpRequest(ctx,(FullHttpRequest)msg);
			
		//websocket请求
		}else if(msg instanceof WebSocketFrame){
			handleWebsocketFrame(ctx, (WebSocketFrame)msg);
		}
	}	
	
	/**
	 * 处理http协议请求的方法
	 * @param ctx
	 * @param request
	 * @throws Exception 
	 */
	private void handleHttpRequest(ChannelHandlerContext ctx, FullHttpRequest request) {
		//判断http请求路径是否合法
		if(!request.decoderResult().isSuccess() && !("websocket".equals(request.headers().get("Upgrade")))){
			sendHttpResponse(ctx, request, new DefaultFullHttpResponse(HttpVersion.HTTP_1_1,HttpResponseStatus.BAD_REQUEST));
			return;
		//websocket请求
		}else if(request.decoderResult().isSuccess() && ("websocket".equals(request.headers().get("Upgrade")))){
			
			//获取http请求路径
			QueryStringDecoder queryStringDecoder = new QueryStringDecoder(request.uri());
			String path = queryStringDecoder.path();
			if("/test".equals(path)){
				WebSocketServerHandshakerFactory wsFactory = new WebSocketServerHandshakerFactory(
						"ws://localhost:8888/test", null, false);
				handshaker = wsFactory.newHandshaker(request);
				if (handshaker == null) {
					WebSocketServerHandshakerFactory.sendUnsupportedVersionResponse(ctx.channel());
				} else {
					String userId = queryStringDecoder.parameters().get("userId").get(0);
					if( userId != null){
						handshaker.handshake(ctx.channel(), request);
						Attribute<String> attr = ctx.channel().attr(WsConstant.USER_ID_KEY);
						attr.set(userId);
						WebScocketChannelGroup.add(ctx.channel());
					}
					
				}
			}
			return;
		}
		//获取http请求路径
		QueryStringDecoder queryStringDecoder = new QueryStringDecoder(request.uri());
		String path = queryStringDecoder.path();
		
		//get请求类型参数
		Map<String, List<String>> params = null;
		//post请求类型参数
        HttpPostRequestDecoder decoder = null;
		//获取请求参数
		 if (request.method() == HttpMethod.GET) {
             params = queryStringDecoder.parameters();
         } else if (request.method() == HttpMethod.POST) {
             decoder = new HttpPostRequestDecoder(request);
         } else {
             try {
				throw new Exception("HTTP method 不支持:" + request.method().name());
			} catch (Exception e) {
				e.printStackTrace();
			}
         }
		//响应内容  
		ByteBuf content = null;
		//处理默认请求
	   if ("/".equals(path)) {
           content = getContent(1,"netty http server!");
	   }//没有对应的url
	   else{
		   content = getContent(-1,"404");
	   }
	   
	   //构建http response对象
	   if(content != null){
		   FullHttpResponse res = new DefaultFullHttpResponse(HTTP_1_1, OK, content);
	       //设置响应头信息,CONTENT_TYPE,content
	       res.headers().set(HttpHeaderNames.CONTENT_TYPE, "application/json; charset=UTF-8");
	       res.headers().setInt(HttpHeaderNames.CONTENT_LENGTH,content.readableBytes());
	       sendHttpResponse(ctx, request, res);
	   }
      
	}
	
	/**
	 * 获取get参数
	 * @param name
	 * @param params
	 * @return
	 */
    public String getParam(String name, Map<String, List<String>> params) {
        if (params == null) {
            return null;
        }
        List<String> retLst = params.get(name);
        if (retLst == null) {
            return null;
        }
        if (!retLst.isEmpty()) {
            return retLst.get(0);
        } else {
            return null;
        }
    }
	
	/**
	 * 处理WebSocket协议类型的方法
	 * @param ctx
	 * @param frame
	 */
	private void handleWebsocketFrame(ChannelHandlerContext ctx, WebSocketFrame frame) {
		//判断是否是关闭链路指令
		if(frame instanceof CloseWebSocketFrame) {
			handshaker.close(ctx.channel(), (CloseWebSocketFrame) frame.retain());
			return;
		}
		//判断是否是ping消息
		if(frame instanceof PingWebSocketFrame) {
			ctx.channel().writeAndFlush(new  PongWebSocketFrame(frame.content().retain()));
			return;
		}
		//本例程只支持文本消息
		if(! (frame instanceof TextWebSocketFrame)) {
			throw new  UnsupportedOperationException(String.format("%s frame types not supported", frame.getClass().getName()));
		}
		
		//返回应答消息
		String reqText = ((TextWebSocketFrame)frame).text();
		log.info(reqText);
		JSONObject reqJson = JSONObject.parseObject(reqText);
		if(reqJson != null && !StringUtils.isEmpty(reqJson.getString("sendTo")) && !StringUtils.isEmpty(reqJson.getString("userId"))
				&& !StringUtils.isEmpty(reqJson.getString("msg"))) {
			WebScocketChannelGroup.send(reqJson.getString("sendTo"), reqJson.getString("msg"),reqJson.getString("userId"));
		}

	}
	
	/**
	 * 发送响应方法
	 * @param ctx
	 * @param request
	 * @param response
	 */
	private static void sendHttpResponse(ChannelHandlerContext ctx, FullHttpRequest request,FullHttpResponse response) {
		//返回应答给客户端
		if(response.status().code() != 200) {
			ByteBuf buf = Unpooled.copiedBuffer(response.status().toString(),CharsetUtil.UTF_8);  
			response.content().writeBytes(buf);
			buf.release();
			response.headers().setInt(HttpHeaderNames.CONTENT_LENGTH,response.content().readableBytes());
		}
		
		ChannelFuture f = ctx.channel().writeAndFlush(response);
	}
	
    private static ByteBuf getContent(int status,String cnt) {
    	 Map<String, Object> result = new LinkedHashMap<String, Object>();
    	 result.put("status", status);
    	 result.put("info", cnt);
        return Unpooled.copiedBuffer(JSON.toJSONString(result), CharsetUtil.UTF_8);
    }
    
    /**
     * 连接通道进行关闭时执行事件
     * @param ctx
     */
    public void channelInactive(ChannelHandlerContext ctx){
    	WebScocketChannelGroup.discard(ctx.channel());
    	ctx.fireChannelInactive();
    }
    
	/**
	 * 抓住异常，当发生异常的时候，可以做一些相应的处理，比如打印日志、关闭链接
	 */
	public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause)
			throws Exception {
		 log.info("异常退出:"+cause.getMessage());
		 ctx.close();
	}

	

}
